import Axios, { type AxiosInstance, type AxiosRequestConfig } from "axios";
import type {
  PureHttpError,
  PureHttpRequestConfig,
  PureHttpResponse,
  RequestMethods
} from "./types.d";
import { stringify } from "qs";
import NProgress from "../progress";
import { formatToken, getToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";
import type { Result } from "@/api/common";
import JSONBigInt from "json-bigint-string";

BigInt.prototype["toJSON"] = function () {
  return this.toString();
};
// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  // 请求超时时间
  timeout: 10000,
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
  paramsSerializer: function (params) {
    return stringify(params, { arrayFormat: "repeat" });
  },
  baseURL: "/api",
  transformResponse: [
    function (data) {
      // 对 data 进行任意转换处理
      try {
        return JSONBigInt.parse(data);
      } catch (err) {
        return data;
      }
    }
  ]
  // transformRequest: [
  //   function (data) {
  //     // 对 data 进行任意转换处理
  //     try {
  //       let resp = JSONBig.parse(data);
  //       console.log({ resp });

  //       return resp;
  //     } catch (err) {
  //       return data;
  //     }
  //   }
  // ]
};

class PureHttp {
  constructor() {
    this.httpInterceptorsRequest();
    this.httpInterceptorsResponse();
  }

  /** token过期后，暂存待执行的请求 */
  private static requests = [];

  /** 防止重复刷新token */
  private static isRefreshing = false;

  /** 初始化配置对象 */
  private static initConfig: PureHttpRequestConfig = {};

  /** 保存当前Axios实例对象 */
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);

  /** 重连原始请求 */
  private static retryOriginalRequest(config: PureHttpRequestConfig) {
    return new Promise(resolve => {
      PureHttp.requests.push((token: string) => {
        config.headers["Authorization"] = formatToken(token);
        resolve(config);
      });
    });
  }

  /** 请求拦截 */
  private httpInterceptorsRequest(): void {
    PureHttp.axiosInstance.interceptors.request.use(
      async (config: PureHttpRequestConfig): Promise<any> => {
        // 开启进度条动画
        NProgress.start();
        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof config.beforeRequestCallback === "function") {
          config.beforeRequestCallback(config);
          return config;
        }
        if (PureHttp.initConfig.beforeRequestCallback) {
          PureHttp.initConfig.beforeRequestCallback(config);
          return config;
        }
        /** 请求白名单，放置一些不需要token的接口（通过设置请求白名单，防止token过期后再请求造成的死循环问题） */
        const whiteList = ["/auth/refresh_token", "/auth/login"];
        return whiteList.find(url => url === config.url)
          ? config
          : new Promise(resolve => {
              const data = getToken();
              if (data) {
                const now = new Date().getTime();
                const expired = parseInt(String(data.expires)) - now <= 0;
                console.log({ expired });
                if (expired) {
                  if (!PureHttp.isRefreshing) {
                    PureHttp.isRefreshing = true;
                    // token过期刷新
                    useUserStoreHook()
                      .handRefreshToken({ refreshToken: data.refreshToken })
                      .then(res => {
                        const token = res.data.access_token;
                        config.headers["Authorization"] = formatToken(token);
                        PureHttp.requests.forEach(cb => cb(token));
                        PureHttp.requests = [];
                      })
                      .finally(() => {
                        PureHttp.isRefreshing = false;
                      });
                  }
                  resolve(PureHttp.retryOriginalRequest(config));
                } else {
                  config.headers["Authorization"] = formatToken(
                    data.accessToken
                  );
                  resolve(config);
                }
              } else {
                resolve(config);
              }
            });
      },
      error => {
        return Promise.reject(error);
      }
    );
  }

  /** 响应拦截 */
  private httpInterceptorsResponse(): void {
    const instance = PureHttp.axiosInstance;
    instance.interceptors.response.use(
      (response: PureHttpResponse) => {
        const $config = response.config;
        // 关闭进度条动画
        NProgress.done();

        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof $config.beforeResponseCallback === "function") {
          $config.beforeResponseCallback(response);
          return response.data;
        }
        if (PureHttp.initConfig.beforeResponseCallback) {
          PureHttp.initConfig.beforeResponseCallback(response);
          return response.data;
        }
        if (response.request?.responseType == "blob") {
          return response;
        }
        return response.data;
      },
      (error: PureHttpError) => {
        const $error = error;
        $error.isCancelRequest = Axios.isCancel($error);
        // 关闭进度条动画
        NProgress.done();
        // 所有的响应异常 区分来源为取消请求/非取消请求
        return Promise.reject($error);
      }
    );
  }

  /** 通用请求工具函数 */
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    axiosConfig?: PureHttpRequestConfig
  ): Promise<T> {
    const config = {
      method,
      url,
      ...param,
      ...axiosConfig
    } as PureHttpRequestConfig;
    // 单独处理自定义请求/响应回调
    return new Promise((resolve, reject) => {
      PureHttp.axiosInstance
        .request(config)
        .then((response: undefined) => {
          resolve(response);
        })
        .catch(error => {
          reject(error);
        });
    });
  }

  /** 单独抽离的post工具函数 */
  public post<P>(
    url: string,
    params?: AxiosRequestConfig,
    config?: PureHttpRequestConfig
  ): Promise<Result<P>> {
    return this.request<Result<P>>("post", url, params, config);
  }

  /** 单独抽离的put工具函数 */
  public put<P>(
    url: string,
    params?: AxiosRequestConfig,
    config?: PureHttpRequestConfig
  ): Promise<Result<P>> {
    return this.request<Result<P>>("put", url, params, config);
  }

  /** 单独抽离的get工具函数 */
  public get<P>(
    url: string,
    params?: AxiosRequestConfig,
    config?: PureHttpRequestConfig
  ): Promise<Result<P>> {
    return this.request<Result<P>>("get", url, params, config);
  }

  /** 单独抽离的delete工具函数 */
  public delete<P>(
    url: string,
    params?: AxiosRequestConfig,
    config?: PureHttpRequestConfig
  ): Promise<Result<P>> {
    return this.request<Result<P>>("delete", url, params, config);
  }

  public download(
    url: string,
    params?: AxiosRequestConfig,
    config?: PureHttpRequestConfig
  ) {
    PureHttp.axiosInstance
      .get(url, { responseType: "blob", ...params, ...config })
      .then(response => {
        // let type = response.headers["Content-Type"] as string;
        const blob = new Blob([response.data], { type: response.data.type });
        const url = URL.createObjectURL(blob);

        const filenameRegex = /filename\*=(UTF-8'')?([^;\n]*)/i;
        const matches = filenameRegex.exec(
          response.headers["content-disposition"]
        );
        let filename = "downloaded_file";

        if (matches != null && matches[2]) {
          filename = decodeURIComponent(matches[2]);
        }

        const link = document.createElement("a");
        link.href = url;
        link.download = filename; // 指定下载文件的名称
        link.style.display = "none";

        document.body.appendChild(link);
        link.click();

        // 清理临时URL和<a>标签
        URL.revokeObjectURL(url);
        document.body.removeChild(link);
      });
  }
}

export const http = new PureHttp();
